package com.xtaller.sdboot.controller;

import com.alibaba.fastjson.JSONObject;
import com.xtaller.sdboot.entity.*;
import com.xtaller.sdboot.service.*;
import com.xtaller.sdboot.util.CacheUtil;
import com.xtaller.sdboot.util.PermissionUtil;
import com.xtaller.sdboot.vo.LoginVO;
import com.xtaller.sdboot.vo.PasswordVO;
import com.xtaller.sdboot.core.R;
import com.xtaller.sdboot.core.Status;
import com.xtaller.sdboot.utils.annotation.Common;
import com.xtaller.sdboot.utils.annotation.Login;
import com.xtaller.sdboot.utils.annotation.Token;
import com.xtaller.sdboot.core.base.BaseEntity;
import com.xtaller.sdboot.core.base.OriginalController;
import com.xtaller.sdboot.utils.captcha.CaptchaClient;
import com.xtaller.sdboot.utils.captcha.bean.CaptchaBean;
import com.xtaller.sdboot.utils.captcha.service.ImageTypeEnum;
import com.xtaller.sdboot.utils.captcha.strategy.SimpleCaptchaStrategy;
import com.xtaller.sdboot.utils.helper.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiOperation;
import lombok.var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author : Taller
 * @date : 2020/2/24 00:46
 * @Description : 登录控制器
 */
@RestController
@RequestMapping("/v1/platform")
public class PlatformLoginController extends OriginalController {
    @Autowired private IamAccountService accountService;
    @Autowired private IamUserApplicationService userApplicationService;
    @Autowired private IamUserService userService;
    @Autowired private IamApplicationService applicationService;
    @Autowired private IamApplicationPermissionService applicationPermissionService;
    @Autowired private IamPermissionService permissionService;
    @Autowired private IamModuleService moduleService;
    @Autowired private IamUserRoleService userRoleService;
    @Autowired private IamRolePermissionService rolePermissionService;
    @Autowired private CacheUtil cacheUtil;

    @Autowired private PermissionUtil permissionUtil;

    /**
     * 退出系统
     * */
    @ApiOperation("退出系统")
    @Token
    @GetMapping("/iam/ext")
    public Object ext(){
        var tokenKey = "account-" + userId.get();
        cacheUtil.delete(tokenKey);
        return R.ok("安全退出系统");
    }

    /**
     * 获取验证码
     * */
    @ApiOperation("生成base64验证码图片")
    @Common
    @GetMapping("/iam/code")
    public Object captchaImg() throws Exception{
        CaptchaClient simpleCaptchaClient = CaptchaClient.create().
                captchaStrategy(new SimpleCaptchaStrategy())
                .transform(true)
                .imageType(ImageTypeEnum.GIF)
                .number(4)
                .build();

        CaptchaBean captchaBean = simpleCaptchaClient.generate();

        var val = MapHelper.create("platform", "iam").put("code", captchaBean.getResult());
        var jwt = JwtHelper.createJWT("", val, 180 * 1000);
        var img = JsonHelper.create("token", jwt)
                .put("img", StrHelper.join("data:image/jpeg;base64,", captchaBean.getBase64()));
        return R.ok("生成成功", img);
    }

    /**
     * 用户登录
     * */
    @ApiOperation("后台登录")
    @Common
    @PostMapping("/iam/login")
    public Object login(@Valid @RequestBody LoginVO vo){
        // 校验Token的jwt信息
        var jwt = JwtHelper.validateJWT(token.get());
        if(!jwt.isSuccess()){
            return R.error(Status.FAIL_VALIDATION, "验证码认证时效已经失效");
        }
        var code = subject.get().getString("code");
        if(!ValidatorHelper.equals(code.toLowerCase(), vo.getCode().toLowerCase())){
            return R.error(Status.FAIL_VALIDATION, "验证码有误");
        }
        var account = getAccountByMobile(vo.getMobile());
        if(account == null) {
            return R.error(Status.FAIL_VALIDATION, "用户名或密码错误");
        }
        if(account.getState() != 1){
            return R.error(Status.FAIL_VALIDATION, "账户已被禁止登录");
        }
        if(!validatorPwd(account, vo.getPassword())){
            return R.error(Status.FAIL_VALIDATION, "密码或用户名错误");
        }

        var loginInfo = getUserByAccount(account, "");
        return R.ok(loginInfo);
    }

    @ApiOperation("密码修改")
    @Token
    @PostMapping("/iam/password")
    public Object resetPassword(@Valid @RequestBody PasswordVO object){
        var uid = userId.get();
        var user = userService.get(uid);
        if(user == null) {
            return R.error(Status.FAIL_NOT_FOUND, "用户数据异常");
        }
        var qw = new QueryWrapper<IamAccount>();
        qw.lambda().eq(IamAccount::getUserId, uid)
                .eq(IamAccount::getUserType, "iam")
                .eq(IamAccount::getAuthType, "pwd");
        var account = accountService.get(qw);
        var check = checkPwd(account, object.getOldPwd());
        if(!check){
            return R.error(Status.FAIL_EXCEPTION, "旧密码错误");
        }
        var salt = StrHelper.newRandomNum(6);
        var pwd = MD5Helper.encode(StrHelper.join(object.getNewPwd(), salt));
        account.setAuthSecret(pwd);
        account.setAuthSalt(salt);

        var model = accountService.model(account);
        if(!model){
            return R.error(Status.FAIL_EXCEPTION, "密码修改失败");
        }

        return R.ok("密码修改成功,请重新登录.");
    }

    /**
     * 密码校验
     * */
    private boolean checkPwd(IamAccount account, String userPwd) {
        // 判断密码正确性
        var oldPwd = account.getAuthSecret();
        var salt = account.getAuthSalt();
        var checkPwd = MD5Helper.encode(StrHelper.join(userPwd, salt));
        if (ValidatorHelper.equals(oldPwd, checkPwd)) {
            return true;
        }
        return false;
    }

    /**
     * 校验iam token
     * */
    @ApiOperation("校验iam token 获取应用权限")
    @Login
    @GetMapping("/iam/token")
    public Object validatorToken(){
        // 检查当前用户是否是超管
        var uaQw = new QueryWrapper<IamUserApplication>();
        uaQw.lambda()
                .eq(IamUserApplication::getAppId, applicationId.get())
                .eq(IamUserApplication::getUserId, userId.get())
                .eq(IamUserApplication::getIsAdmin, 1);
        var apps = userApplicationService.getList(uaQw);
        var isAdmin = apps.size() > 0 ? true : false;

        if(isAdmin){
            // 应用所有权限 getPermissions(applicationId.get());
            var permissions = permissionUtil.getAppPermission(applicationId.get());
            if(permissions == null){
                return R.ok(JsonHelper.list());
            }
            permissionUtil.updateAppCache(userId.get(), 1, Arrays.asList(""));
            return R.ok(formatPermission(permissions));
        }else{
            // 获取当前用户的角色
            var urQw = new QueryWrapper<IamUserRole>();
            urQw.lambda()
                    .eq(IamUserRole::getUserId, userId.get())
                    .eq(IamUserRole::getAppId, applicationId.get());
            var urs = userRoleService.getList(urQw);
            if(urs.size() > 0){
                var roleIds = urs.stream().map(x -> x.getRoleId()).collect(Collectors.toList())
                        .stream().distinct().collect(Collectors.toList());
                if(roleIds.size() == 0){
                    return R.ok(JsonHelper.list());
                }
                var permissions = permissionUtil.getRolePermission(roleIds); // getRolePermissions(urs);
                // todo 写入权限信息 20200306去掉缓存

                permissionUtil.updateAppCache(userId.get(), 0, roleIds);
                return R.ok(formatPermission(permissions));
            }
            return R.ok(JsonHelper.list());
        }

    }

    /**
     * 进入应用
     * */
    @ApiOperation("进入应用")
    @Login
    @GetMapping("/iam/application/{id}")
    public Object enterApplication(@PathVariable("id") String id){
        var app = applicationService.get(id);
        if(app == null){
            return R.error(Status.FAIL_NOT_FOUND, "应用id异常");
        }
        var info = subject.get();
        var qw = new QueryWrapper<IamAccount>();
        qw.lambda()
                .eq(IamAccount::getAuthAccount, info.getString("authAccount"))
                .eq(IamAccount::getUserType, info.getString("userType"))
                .eq(IamAccount::getUserId, info.getString("userId"));
        var data = accountService.getList(qw);
        if(data.size() > 0){
            var loginInfo = getUserByAccount(data.get(0), id);
            return R.ok(loginInfo);
        }
        return R.error(Status.FAIL_EXCEPTION, "用户信息错误");
    }

    @ApiOperation("获取应用列表")
    @Login
    @GetMapping("/application")
    public Object getApplicationList(){
        var list = JsonHelper.list();
        var uaQw = new QueryWrapper<IamUserApplication>();
        uaQw.lambda().eq(IamUserApplication::getUserId, userId.get());
        var uas = userApplicationService.getList(uaQw);
        if(uas.size() > 0){
            var appIds = uas.stream().map(x -> x.getAppId()).collect(Collectors.toList());

            var apps = applicationService.getByIds(appIds);
            if(apps.size() > 0){
                apps.stream().filter(x -> x.getState() == 1).forEach(x -> {
                    var app = JsonHelper.create("code", x.getCode())
                                .put("name", x.getName())
                                .put("panel", x.getPanel())
                                .put("icon", x.getIcon())
                                .put("id", x.getId());
                    list.add(app);
                });
            }
        }
        return R.ok(list);
    }

    /**
     * 用户数据处理
     * */
    private JSONObject getUserByAccount(IamAccount account, String appId){
        var user = userService.get(account.getUserId());
        var userData = JsonHelper.create("realName", user.getRealName())
                            .put("mobile", user.getMobile())
                            .put("gender", user.getGender())
                            .put("email", user.getEmail())
                            .put("avatar", user.getAvatar())
                            .put("ext", user.getJsonExt());
        var token = createJwt(account, appId);
        var data = JsonHelper.create("user", userData)
                    .put("token", token);
        // todo 写入缓存
        var cacheKey = StrHelper.join("account-", account.getUserId());
        var auth = JsonHelper.create("token", token);
        cacheUtil.set(cacheKey, auth);
        // todo 写入登录日志

        return data;
    }
    /**
     * 生成JWT认证数据
     * */
    private String createJwt(IamAccount account, String appId) {
        var data = MapHelper.create("userId", account.getUserId())
                .put("time", TimeHelper.getTimestamp())
                .put("userType", account.getUserType())
                .put("unionId", account.getUnionId())
                .put("openId", account.getOpenId())
                .put("authAccount", account.getAuthAccount())
                .put("applicationId", appId);
        return JwtHelper.createJWT(
                    account.getUserId(),
                    data,
                    0);
    }
    /**
     * 根据登录手机号读取登录账户信息
     * */
    private IamAccount getAccountByMobile(String mobile){
        var qw = new QueryWrapper<IamAccount>();
        qw.lambda().eq(IamAccount::getUserType, "iam")
                .eq(IamAccount::getAuthAccount, mobile);
        var list = accountService.getList(qw);
        return list.size() > 0 ? list.get(0) : null;
    }

    /**
     * 登录密码验证
     * */
    private boolean validatorPwd(IamAccount account, String pwd){
        // 判断密码正确性
        var oldPwd = account.getAuthSecret();
        var salt = account.getAuthSalt();
        var checkPwd = MD5Helper.encode(StrHelper.join(pwd, salt));

        if (ValidatorHelper.equals(oldPwd, checkPwd)) {
            return true;
        }
        return false;
    }

    /**
     * 权限数据处理
     * */
    private List<JSONObject> formatPermission(List<IamPermission> permissions){
        // 根据模块进行排序
        var moduleIds = permissions
                .stream().map(x -> x.getModuleId()).collect(Collectors.toList())
                .stream().distinct().collect(Collectors.toList());
        var qw = new QueryWrapper<IamModule>();
        qw.lambda()
                .eq(IamModule::getState, 1)
                .in(BaseEntity::getId, moduleIds)
                .orderByDesc(IamModule::getSortId);
        var modules = moduleService.getList(qw);

        var data = JsonHelper.list();
        modules.stream().forEach(z -> {
            var list = permissions.stream().filter(y -> y.getModuleId().equals(z.getId())).collect(Collectors.toList());
            var funs = JsonHelper.list();
            var dataFuns = list.stream().filter(x -> x.getModuleId().equals(x.getParentId())).collect(Collectors.toList());
            var dataPers = list.stream().filter(x -> !x.getModuleId().equals(x.getParentId())).collect(Collectors.toList());
            Collections.sort(dataFuns, new IamPermissionSortComparetor());
            dataFuns.stream().forEach(x -> {
                var fun = JsonHelper.create("id", x.getId())
                        .put("url", x.getUri())
                        .put("icon", x.getIcon())
                        .put("event", "")
                        .put("name", x.getName())
                        .put("position", "0")
                        .put(   "sub", findMenu(dataPers, x.getId()));
                funs.add(fun);
            });
            data.addAll(funs);
        });

        return data;
    }


    /**
     * 找菜单
     * */
    private List<JSONObject> findMenu(List<IamPermission> permissions , String parentId){
        var subs = JsonHelper.list();
        var funs = permissions.stream().filter(x -> x.getGroups().equals("menu")).collect(Collectors.toList());
        var auths = permissions.stream().filter(x -> x.getGroups().equals("auth")).collect(Collectors.toList());
        Collections.sort(funs, new IamPermissionSortComparetor());
        funs.stream().forEach(x -> {
            if(x.getParentId().equals(parentId)){
                var sub = JsonHelper.create("id", x.getId())
                        .put("url", x.getUri())
                        .put("icon", "")
                        .put("event", "")
                        .put("name", x.getName())
                        .put("position", "0")
                        .put("permission", findPermission(auths, x.getId()));
                subs.add(sub);
            }
        });
        return subs;
    }

    /**
     * 找权限
     * */
    private List<JSONObject> findPermission(List<IamPermission> permissions , String parentId){
        var pers = JsonHelper.list();
        Collections.sort(permissions, new IamPermissionPositionComparetor());
        permissions.stream().forEach(x -> {
            if(x.getParentId().equals(parentId)) {
                var per = JsonHelper.create("id", x.getId())
                        .put("url", "")
                        .put("icon", "")
                        .put("event", x.getEvent())
                        .put("name", x.getName())
                        .put("sortId", x.getSortId())
                        .put("type", x.getPosition());
                pers.add(per);
            }
        });
        return pers;
    }

    /**
     * 给权限排序
     * */
    private class IamPermissionPositionComparetor implements Comparator<IamPermission> {
        @Override
        public int compare(IamPermission h1, IamPermission h2) {
            if (h1.getPosition() < h2.getPosition()) {
                return 1;
            } else if (h1.getPosition().equals(h2.getPosition())) {
                return 0;
            } else {
                return -1;
            }
        }
    }
    private class IamPermissionSortComparetor implements Comparator<IamPermission> {
        @Override
        public int compare(IamPermission h1, IamPermission h2) {
            if (h1.getSortId() < h2.getSortId()) {
                return 1;
            } else if (h1.getSortId().equals(h2.getSortId())) {
                return 0;
            } else {
                return -1;
            }
        }
    }
    private class IamModuleSortComparetor implements Comparator<IamModule> {
        @Override
        public int compare(IamModule h1, IamModule h2) {
            if (h1.getSortId() < h2.getSortId()) {
                return 1;
            } else if (h1.getSortId().equals(h2.getSortId())) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    /**
     * 应用所有权限
     * */
    private List<IamPermission> getPermissions(String appId){
        var qw = new QueryWrapper<IamApplicationPermission>();
        qw.lambda().eq(IamApplicationPermission::getAppId, appId);
        var permissions = applicationPermissionService.getList(qw);
        var ids = permissions.stream().map(x -> x.getPermissionId()).collect(Collectors.toList());
        var permissionQw = new QueryWrapper<IamPermission>();
        permissionQw.lambda()
                .eq(IamPermission::getType, "admin")
                .in(IamPermission::getId, ids)
                .orderByDesc(IamPermission::getModuleId)
                .orderByDesc(IamPermission::getSortId)
                .orderByDesc(IamPermission::getParentId)
                .orderByDesc(IamPermission::getState)
                .orderByDesc(IamPermission::getGroups)
                .orderByDesc(IamPermission::getPosition);
        return permissionService.getList(permissionQw);
    }

    /**
     * 应用角色权限
     * */
    private List<IamPermission> getRolePermissions(List<IamUserRole> urs){
        var roleIds = urs.stream().map(x -> x.getRoleId()).collect(Collectors.toList())
                .stream().distinct().collect(Collectors.toList());
        // 获取权限
        var rpQw = new QueryWrapper<IamRolePermission>();
        rpQw.lambda().in(IamRolePermission::getRoleId, roleIds);
        var rps = rolePermissionService.getList(rpQw);
        var permissionsIds = rps.stream().map(x -> x.getPermissionId()).collect(Collectors.toList())
                .stream().distinct().collect(Collectors.toList());

        var permissionQw = new QueryWrapper<IamPermission>();
        permissionQw.lambda()
                .eq(IamPermission::getType, "admin")
                .in(IamPermission::getId, permissionsIds)
                .orderByDesc(IamPermission::getModuleId)
                .orderByDesc(IamPermission::getSortId)
                .orderByDesc(IamPermission::getParentId)
                .orderByDesc(IamPermission::getState)
                .orderByDesc(IamPermission::getGroups)
                .orderByDesc(IamPermission::getPosition);
        return permissionService.getList(permissionQw);
    }
}
